Vsync Issues - half frame rate with vSyncCount of 1

Hello,

I’ve been chasing down a weird issue with vSync for the past couple of weeks. It seems that whenever I have vSync enabled (either via quality settings, or using QualitySettings.vSyncCount) the exported Windows build always uses half the vertical refresh rate of the monitor it’s rendering to.
That is, if I’m running on a 75hz monitor, the game appears at around 37 fps. If I use a vSync count higher than 1, the refresh rate reduces further. This behavior remains consistent when tried on a variety of other machines.

This is counter to how the documentation describes the behavior of vSync count. I would expect a vSyncCount of 1 to match the vertical refresh rate of the monitor.

This is NOT a performance bottleneck on either the CPU or GPU. The exe is fully capable of pushing 200fps when running uncapped.

I have attempted to profile exported builds, but when I try to profile a build, I get log spam;
GfxDeviceD3D11Base::PresentFrame() wat called without calling CfxDeviceD3D11Base::WaitForLastPresentationAndGetTimestamp() first. This will result in increased input latency...
which makes the build run exceptionally slow due to the massive number of messages.
This is what the profiler for CPU usage looks like;


The total Scripts time is less than 1ms. To me, it seems like Unity is doing an excessive amount of waiting around for vSync , as if somehow has the wrong value for what the refresh rate should be.

For some extra context;

  • I’m using Spine Unity for 2d character animation. Otherwise, it’s just built-in or Unity-provided plugins.
  • I’m using Unity 2022.3.15. I have experimented with a variety of different editor versions with no success.
  • Creating a clean project with nothing else will work with vSync as expected.
  • Every possible resolution and presentation option combo has been tried.
  • I’ve also experimented with forcing different graphics APIs with no success.

I’m fairly convinced that there’s something I’m doing to cause this, but am at a loss as to what I should be looking at. For the moment, I can cap the frame rate at the monitor refresh rate to get close, but that can still show tearing. If I can get vSync working properly, that would be ideal, but of course we don’t live in an ideal world.
Tips/Suggestions are appreciated.

Is perhaps “half vsync” enforced by the graphics driver?
Is it a Gsync or Freesync monitor?
How did you measure Fps? Try an asset like Graphy (free) to get an in-scene, reliable fps display.

Try 2022.3.55 and if that generally works, stay on that. You don’t want to use the early patch releases for too long.

When trying “different editor versions” I hope you did so in a copy of the project because particularly going back to an earlier version can be destructive. It also warrants deleting the Library folder at a minimum.

I thought that was exactly what vsync actually did… I remember someone in a unity video explaining that vysnc will halve fps…

It will do so if and only if the target framerate is not reached.

Say you have 60 Hz monitor with Vsync enabled, then it will render 60 fps if the frame time is 0.0166666 ms or less. But as soon as frame time (once or consistently) is 0.017 ms then your framerate will instantly be halved because vsync forces you to wait for 99% of the second vsync because during the first vsync the new frame was just not ready in time.

In effect, a steady vsync-off framerate of 59 fps would become 30 fps with vsync enabled.

that sounds familiar, it takes only 1 tiny fart and its chunked it

Thanks for your response.

I’ve ended up solving my own problem. It was (as per usual) something I caused. I had some experimental code hiding in one of my helper code libraries. The code was manipulating the native player loop for various reasons. Disabling that code resolved my issues entirely. That’s the last time I follow a video tutorial claiming to have “GameObject-free” update hooks.

1 Like

Let me guess … that video used “DefaultPlayerLoop”, not “CurrentPlayerLoop”? Because that will omit some non-default Player Loop hooks. Packages might add their own loop hooks. Such a PlayerLoop would then not run all the project’s features properly, and issues arising from this could be zero or anything.