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

Vsync broken? #1184

Closed
xanather opened this issue Jan 15, 2013 · 58 comments
Closed

Vsync broken? #1184

xanather opened this issue Jan 15, 2013 · 58 comments
Assignees

Comments

@xanather
Copy link
Contributor

This is exactly the same as (which I also posted)

#1085

I haven't played around with MonoGame for a little while but I came back and the problem still exists. I'm not sure if its just windows only or other platforms are affected by this.

Whenever I debug a new windows MonoGame project the TargetElaspedTime of the Game class returns a tick rate of 170,000 ticks, not 166,666 ticks. Which I think results in 60fps not being achieved and Vsync not available for most monitors.

Looking at the actual MonoGame code that sets this value:

private TimeSpan _targetElapsedTime = TimeSpan.FromSeconds(1 / DefaultTargetFramesPerSecond);

When I actually try that method on .net 4.5 it surely does return 170,000 ticks.
Does this need to be changed?

@KonajuGames
Copy link
Contributor

I tried a few different things there. The only one that worked was

private TimeSpan _targetElapsedTime =
TimeSpan.FromTicks((long)10000000 /
(long)DefaultTargetFramesPerSecond);

It would appear that all of the TimeSpan.From*(double) methods round to
three decimal places. Using FromTicks() appears to allow more precision.

@Mephistofeles
Copy link

Yeah, this should be changed to TimeSpan.FromTicks.
http://msdn.microsoft.com/en-us/library/system.timespan.fromticks.aspx
This is the only way to achieve exactly 16.66 ms.

From MSDN:
"The value parameter is converted to milliseconds, which is converted to ticks, and that number of ticks is used to intialize the new TimeSpan. Therefore, value will only be considered accurate to the nearest millisecond. "

KonajuGames added a commit that referenced this issue Jan 15, 2013
Fixes issue #1184
TimeSpan.FromSeconds() has an accuracy of 3 decimal places only.  TimeSpan.FromTicks() is more accurate, resulting in the expected target elapsed time of 0.0166667 rather than 0.017.

Reference: http://msdn.microsoft.com/en-us/library/system.timespan.fromticks.aspx
@bonesoul
Copy link
Contributor

I still can't seem to disable vsync with monogame?

@tomspilman
Copy link
Member

What platform? There are certain platforms where you cannot... like Windows Store and iOS.

@bonesoul
Copy link
Contributor

@tomspilman WindowsGL and your windesktop-DX branch.

@xanather
Copy link
Contributor Author

I'm going to re-download/rebuild the repository and see if Vsync is achieved now.

@xanather
Copy link
Contributor Author

With Vsync enabled now it reports 59 FPS. I dont know why. My monitor refresh rate is 60Hz (60 fps)? So Vsync is still not happening.

@tomspilman
Copy link
Member

TargetElaspedTime is achieved by sleeping off remainder time... sleep is not exact. We do this instead of blocking because it frees the CPU time for other processes. This is something MS XNA on the desktop does not do... it will chew up all the CPU as long as the Game is in the active state.

I would expect any calculation of actual FPS to be like 59.8 or 59.9 or something.

If you really want to be blocked on VSync... set IsFixedTimeStep to false. Then VSync will block and you will be at a solid 60fps... and at 100% processor usage.

@xanather
Copy link
Contributor Author

Oh I see, are we unable to implement the XNA solution because of legal reasons? or other reasons (not supported on other platforms?)

@KonajuGames
Copy link
Contributor

Is it causing a real issue? Or is it a case of the FPS reporting 59 instead
of 60 (which is most likely due to floating point error in FPS calculation)?

@xanather
Copy link
Contributor Author

It means Vsync isn't achieved, the graphics.SynchronizeWithVerticalRetrace property becomes useless. Both SharpDX.Toolkit and XNA report 60 FPS with Vsync on.

@tomspilman
Copy link
Member

@xanather

are we unable to implement the XNA solution because of legal reasons

We of course cannot legally copy their code... we can copy their behavior. In this case I made the decision to do something better than they do.

If it has a bad side effect then lets see about fixing it.

If you can build a zip with a simple test case that exhibits the issue i can investigate it.

@xanather
Copy link
Contributor Author

No problem, doing that now.

@xanather
Copy link
Contributor Author

I'm new to github and I cant find a attach file function. I just uploaded it to my sites FTP.

http://www.xanather.com/MonoGameTest.zip

Thanks for the help.

My monitor has a refresh rate of 60Hz so enabling graphics.SynchronizeWithVerticalRetrace = true; should make the framerate become a solid 60 per second. It does not, this results in every x amount of time that the ball texture (in the test) becomes jittery for a bit - its just not smooth like it should be in any other vsync enabled presenter.

The reasons why this concerns me so much is because having that sort of problem would not be good in a 2D side scroller (the whole display would become jittery). When I tried the MonoGame 2.5.0.0 windows project this problem did not exist, but like you say in the previous post that project did use 100% of a CPU core.

I think the way SharpDX TK or XNA handled it would be a better solution.

@tomspilman
Copy link
Member

I'm looking at your test case and I do not see any jitter. Where would I see it? How do you know it isn't running at 60fps?

@tomspilman
Copy link
Member

Ok... got a jitter now. I had to switch away from it a few times then I got it for a little bit after.

I see two potential culprits here... Sleep and Stopwatch. I'm investigating.

@xanather
Copy link
Contributor Author

I used fraps to see what the fps is (using that fps overlay).

Yeah, it is hard to tell, but it would really show if it was a fullscreen game with scrolling/moving textures everywhere.

@tomspilman
Copy link
Member

So I have some initial findings.

While there are some fixes to the logic we can do to smooth things out, there is one fundamental issue affecting us.... thread affinity.

What is happening is that Stopwatch can return incorrect time (both elapsed or timestamps) if the thread switches cores between calls. This happens a lot more than you think.... the process thread bounces around like crazy.

@xanather - Try this to verify what I found!

If like in XNA (you can see it in Process Manager) you add this to your Game constructor:

Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1;

You should see the jitter instantly go away almost entirely (i still see one hiccup every few minutes... that is another topic).

Now setting affinity totally sucks.... it means everything is forced to that one core. If you have two instances of your game... or just two instances of a MonoGame/XNA game running on the same PC, they all pile on that one core instead of spreading out across all your cores. This cripples the OS and keeps it from smartly scheduling work.

The other alternative is to not depend on Stopwatch. Our best second choice is Environment.TickCount:

  • Near zero performance overhead
  • None of the Stopwatch "bugs"
  • Overflows in ~29 days
  • 1 millisecond resolution

This would bottleneck us at 1000 FPS. In practice this is fine, but I know some people cry if they don't see +3000 FPS on an empty vsync disabled game. The overflow is the harder issue... while most people won't play a game 29 days straight we should still allow for it (servers for one come to mind).

So what I am thinking is writing a new Timer class. Internally it uses both Stopwatch and TickCount. When you want elapsed time it returns the smaller positive value between the two timers. This should ensure at least millisecond resolution when the process jumps cores and protects against the TickCount 29 day overflow.

Thoughts?

@KonajuGames
Copy link
Contributor

We had used the SetProcessorAffinity() solution on older PC games. You
should be able to just set thread affinity as well.

The overflow in 29 days can be accounted for as well, by checking for a
large negative step in value and adjusting appropriately.

@xanather
Copy link
Contributor Author

@tomspilman ill try that right now

@xanather
Copy link
Contributor Author

I have interesting finds on my computer, it does work (sort of).

It I debug/run the program and don't touch it, it does run at 60 FPS and the jitter is gone (well it happens once every minute like you say as well), but as soon as I start clicking on other windows and going back to the MonoGame window the FPS drops back down to 59 FPS and the jitter is back, does this happen to you?

@xanather
Copy link
Contributor Author

Are you sure time stamp wont suffice? SharpDX TK uses that:
https://github.com/sharpdx/SharpDX/blob/master/Source/SharpDX/TimerTick.cs
Edit1 : and the Game class of the SharpDX TK uses that timer for its tick method:
https://github.com/sharpdx/SharpDX/blob/master/Source/Toolkit/SharpDX.Toolkit.Game/Game.cs
Edit 2: I might actually be wrong because SharpDX TK may also set the affinity as well, where can I find what affinity has been set on task manager? I cant find it?

@tomspilman
Copy link
Member

@xanather

I have started to avoid looking closely at SharpDX Tookit code. I am concerned some of it comes from using reflector on XNA.

Still... no Stopwatch.GetTimestamp() is just as bad... it uses the same hardware performance counter and has the same sort of jump when switching between cores.

@xanather
Copy link
Contributor Author

Ah alright no problem. Well if you do manage to create that timer class that would be great, the disadvantages of setting the affinity seems like a bad solution.

I am still lost as to why I still receive 59 FPS with Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1; after messing with the window a bit.

@xanather
Copy link
Contributor Author

Bumping this issue (if thats ok, otherwise I need to know if there are rules against this), I still think this issue needs to be solved. It really does pretty much make the v-sync properly useless.

@xanather
Copy link
Contributor Author

Right, probably the cause then.

@KonajuGames
Copy link
Contributor

With my current project the XNA version just runs slower when it doesn't
have focus. Haven't done anything special to allow it that I can recall.

@xanather
Copy link
Contributor Author

bump

@tomspilman
Copy link
Member

I still haven't found the time to work on this issue specifically, but we are starting a big new PC project so I will eventually get to it.

@xanather
Copy link
Contributor Author

xanather commented Apr 9, 2013

Cool, I can obviously develop my game around this so it isn't that big of a deal considering the amount of PR's I see right now. I just like to bump it once in a while so it does not get forgotten.

@xanather
Copy link
Contributor Author

bump

3 similar comments
@xanather
Copy link
Contributor Author

bump

@xanather
Copy link
Contributor Author

xanather commented May 7, 2013

bump

@xanather
Copy link
Contributor Author

bump

@xanather
Copy link
Contributor Author

xanather commented Jun 8, 2013

So I managed to fix this myself. In the end I just did (I don't know why I didn't think of this before really...):

TargetElapsedTime = new TimeSpan(163000);

This makes room for the core jumping and any lost time that occurs. The game no longer has any jitter (except for once every minute obviously). For fixed time step games (I know, I know, fixed time step games are frowned upon) I really think this should get properly fixed. I did try messing with the timer recently but to no avail (I made things worse haha). Ill leave this issue open.

Edit: I have a feeling this will produce different results on different processors though.

@xanather
Copy link
Contributor Author

As expected, my change will produce different results on different processors. @tomspilman should I create a whole new issue around this subject (current issue title seems misleading)? My laptop receives much less jitter than my desktop (I have hyper threading on my desktop). Also when I give my game to friends to be tested and I specifically ask them "can you see any fps lag/jitter" and they always reply with nope. Environment.TickCount last I checked also had a 10 millisecond time frame.

@tomspilman
Copy link
Member

FYI.

@RayBatts is working on vsync support for Windows DirectX platforms. We should have a PR for it in the next day or two.

Note this is the ability to disable vsync, but it is a start towards fixing this issue.

@xanather
Copy link
Contributor Author

Thanks, I actually did end up fixing the V-sync issue by using the following code:

        //XNA and SharpDX TK does this to stablize FPS
        if (IsFixedTimeStep && Math.Abs(_accumulatedElapsedTime.Ticks - TargetElapsedTime.Ticks) < (TargetElapsedTime.Ticks >> 6))
        {
            _accumulatedElapsedTime = TargetElapsedTime;
        }

I didn't make a pull request yet because this would affect all platforms and could be a unwanted change for others.

@xanather
Copy link
Contributor Author

xanather commented Aug 3, 2013

@tomspilman what do you think about the code I posted above? I have been using it for the last 2 weeks and it works very well at fixing the FPS at 60 (even when the CPU is under stress and thread jumping may occur).

@tomspilman
Copy link
Member

I did some experiments with it and have some similar/better code. I just haven't submitted a PR for it yet. Instead of throwing away the remainder time... it just realigns it a bit.

@xanather
Copy link
Contributor Author

xanather commented Aug 4, 2013

That seems like a better solution :) I definitely test the changes in my 2D sidescroller that is very sensitive to timings & smooth FPS.

@xanather
Copy link
Contributor Author

Did you end up finishing this PR? Is a submit possible? My solution does work okay at times, but at some times the goes out of align and I don't know how to realign it.

@xanather
Copy link
Contributor Author

Hi,

Just wondering if this ever got looked at. I'm looking to integrate my game back into MonoGame for cross platform support. SharpDX Toolkit FPS timings are pretty much perfect and Vsync works well no matter what monitor your using.

Thanks,
Xanather.

@tomspilman
Copy link
Member

VSync under DirectX platforms works. Don't think Vsync is implemented on any OpenGL platform beyond the mobile ones.

thefiddler added a commit to thefiddler/MonoGame that referenced this issue Apr 15, 2014
@thefiddler
Copy link
Contributor

This appears to be implemented in OpenTKGamePlatform.cs:98

But only used on Linux: GraphicsDeviceManager.cs:525

How do you change vsync in XNA? Does this take place directly or in ApplyState()?

azchohfi pushed a commit to azchohfi/MonoGame that referenced this issue Apr 15, 2014
@xanather
Copy link
Contributor Author

I was primarily talking about the timing issue which results in Vsync being a failure in any circumstance anyway (WinDX is also affected).

It could be fixed since the last time i tried so I will get my game working with MonoGame again soon and report back.

@tomspilman
Copy link
Member

Vsync being a failure in any circumstance anyway

That seems like your opinion... as for us vsync works, no tearing and limited to 60fps.

@xanather
Copy link
Contributor Author

I should have mentioned, this is for fixed timestep games. A variable timestep game works fine since it becomes limited to the screen rate. When you specify a fixed time step game of 60 FPS and you try to apply Vsync to a 60 hz monitor, sometimes the monitor rate can be slightly off and the game becomes limited to whatever monogame's timings are (resulting in often frame jumps/lags, not actual frame tearing). SharpDX Toolkit and XNA are both happy properly syncing the timing to the monitor refresh rate if thats the case.

I would say this is more noticable in 2D sidescrolling games since its basicalling/sliding a moving image and very sensitive to stuff like this.

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

No branches or pull requests

8 participants