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

Added support for vertical syncing via the Windows OS compositor (DWM.) #33414

Merged
merged 3 commits into from
Dec 4, 2019
Merged

Conversation

TerminalJack
Copy link
Contributor

@TerminalJack TerminalJack commented Nov 7, 2019

Some users are reporting bad jitter when running in windowed mode on the
Windows OS. This change will allow the OS' compositor (DWM) to be used for
vsync. The end-user can enable/disable this feature through a project setting,
command-line option, and/or through the API.

fixes #19783
fixes #27211

Bugsquad edit: See this comment for compiled builds incorporating this pull request.

@TerminalJack TerminalJack requested a review from a team as a code owner November 7, 2019 02:31
@TerminalJack
Copy link
Contributor Author

This PR makes this feature an option that will be disabled by default. It can be enabled or disabled from the command-line, a project setting, or from the API.

Command-line options:

--enable-vsync-via-compositor 
--disable-vsync-via-compositor

The command-line options will override the project setting.

Project setting:

display/window/vsync/vsync_via_compositor

The default value is false.

New API property and methods:

Class: OS

bool vsync_via_compositor [default: false]
	set_vsync_via_compositor(value) setter
	is_vsync_via_compositor_enabled() getter

@TerminalJack
Copy link
Contributor Author

This project...

TestDwmFlush.zip

...can be used to test this feature. Note that the stutter/jitter problem seems to be specific to Nvidia GPUs and only occurs while in windowed mode. Sometimes a game will stutter/jitter constantly and other times it will behave fairly well.

On my system (the one affected), the problem can be reproduced by...

  1. Making another window the active window.
  2. Hovering the mouse over an active task on the taskbar. (Which brings up the live preview of that task.)
  3. Dragging another window around the screen.
  4. Moving the mouse constantly.

This is a Windows 10 system with an Nvidia GeForce GTX 1070 GPU.

@Calinou
Copy link
Member

Calinou commented Nov 12, 2019

I can't get this to build on MinGW on Fedora 31 for some reason:

/usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: platform/windows/context_gl_windows.windows.opt.tools.64.o:context_gl_windows.cpp:(.text+0xd8): undefined reference to `DwmIsCompositionEnabled'
/usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: platform/windows/context_gl_windows.windows.opt.tools.64.o:context_gl_windows.cpp:(.text+0x1c1): undefined reference to `DwmFlush'

I'll use MSVC to provide builds for testing, but I'd like to know how to build it with MinGW still 🙂

@bruvzg
Copy link
Member

bruvzg commented Nov 12, 2019

I can't get this to build on MinGW on Fedora 31 for some reason:

MinGW have its own, separate library list in platform/windows/detect.py.

In addition to the line 224, dwmapi should be also added at line 351.

@Calinou
Copy link
Member

Calinou commented Nov 13, 2019

Here are two Windows 64-bit editor builds compiled with MSVC 2019 and target=release_debug:

(Those links will expire around July 2020.)

@starry-abyss
Copy link
Contributor

Tried the Calinou's PR build, works good for me with the new option enabled (and jitters with it disabled).

@TerminalJack
Copy link
Contributor Author

I've attached a couple of screenshots from Microsoft's GpuView utility (available in the Windows ADK.) This is more or less a profiler that focuses on the GPU rather than the CPU. It allows you to see the workflow of GPU-related work from beginning to end.

These screenshots are for a Godot game running on two systems and show this feature both disabled and enabled. There are a couple of things of interest in the screenshots.

This screenshot is from a system that doesn't seem to be affected by the problem...

DwmFlush Intel UHD 620

The first three frames are with the feature turned off. (The blue vertical bars denote the vertical blank period.) The last three frames are with the feature turned on. AC.EXE is the Godot game.

The things of interest to note here are...

  1. With the feature turned off, one of the threads (in ntdll.dll) is at 100% processor utilization. The same thread's processor utilization drops to much more reasonable levels once the feature is enabled.
  2. Either the OS or the video driver has placed a 'Monitored Fence' packet (the bright red block) in the game's device context basically at the point that the 'Present' packet (purple cross-hatched blocks) and 'Present token' packets (purple single-hatched blocks) would normally be placed. The present packet and present token packets don't actually appear until after the compositor has queued its work.
  3. The game's GPU work is being prevented from coming in at the same time as the compositor's (the brown blocks.)

The 'Monitored Fence' packet is likely being used to synchronize with the compositor. It isn't known why one of the threads would require 100% CPU utilization when the vsync_via_compositor feature is disabled but the two are very clearly correlated.

This screenshot is from a system that is affected by this problem...

DwmFlush Nvidia GeForce GTX 1070

Once again, the first three frames are with the feature turned off and the last three are with the feature turned on. The things to note here are:

  1. There are plenty of CPU / GPU resources available. Which is to say that this problem isn't due to lack of resources or any type of bottleneck.
  2. None of the threads are at 100% CPU utilization. Regardless of whether the feature is disabled or enabled.
  3. A 'Monitored Fence' packet doesn't appear in the game's device context and the placement of the 'Present' and 'Present token' packets don't have any relation to the compositor's GPU work.
  4. With the feature disabled, the game's GPU DMA packets happen to come in at the same time that the GPU is processing the compositor's (DWM.EXE) work. (This is for the next frame.)
  5. Enabling the feature prevents the game's GPU packets from coming in at the same time as the compositor's.

@Ranoller
Copy link
Contributor

Ranoller commented Nov 17, 2019

Ok, i tested that and.... you are the man!!!!.

Test (Bad computer, bad card):

Windows 7 64 bit, 2 screen fullHD + 1 HUION tablet in a GeForce GTX 660/PCIe
Intel Core i5-3330 3.00GHz in a ASRock B75 Pro3 with 16GB RAM. (Computer from 2015)

Full Screen:

  • Stutter: none
  • Tearing: yes

Windowed:

  • Stutter: DEFINITELY NONE!!!!!! (no mathers simplicity or complexity of project)
  • Tearing: none!!!!

Well. This can be partially the solution to the Godot stutter problem, at least in windows, and that is too important!!!.

This feature is great, but needs at least one improvement:
Once you tick the option in project settings, VSYNC option needs to be deactivated at the same time, because that two options are incompatible (The same with this option, both option should trigger another as oposite). Like this:
My bad. That was not true.
ToggleTick

And if this could be toogled in runtime, the same at Vsync, this could be AWESOME. (I didn´t try, but i suppose that it can´t be toggled). (ok, is exposed to gdscript, i will test and report)

Well, CC @akien-mga , sorry to ping, but this is not a new feature and is a very important bug fix to: #19783 (comment) and to #25162 and all the recurrent issues about start using godot and seing the stutter in the first simple sprite movement...

I think that this option should be "ON" by default on windows computers, to avoid more issues about the stutter (though can be some issues by windows 7 users about the tearing in full screen).

Thanks a lot for this!!!!

(I will say that there are other things that can do godot stutter and flicker, but the big problem of stutter in the "empty project" is gone in my case)

@TerminalJack
Copy link
Contributor Author

@Ranoller This new option comes into play only when vsync is enabled.

vvc

From what you describe, it sounds like vsync was disabled when you ran your tests. This would account for the tearing in full screen mode.

@Ranoller
Copy link
Contributor

Ranoller commented Nov 18, 2019

Yes. I´m testing more and see that.
Tearing appears with internal Vsync too in certain circunstances (In changing screen modes, sometimes), probably is my system.

A project to test:
Jitter_Stutter_Test.zip
JitterStutterTest
(Only works with this feature merged)

For the merge valoration: This feature is risk 0 (optional and bug fix oriented), and soo useful in the developer side.

I vote for press merge button until it breaks.

(I see this commit tagged 4.0, but we don´t know if vulkan will battle with windows the same as gles2-3, so, if this will be merged, maybe should be merged in the 3.x branch, only an idea)

@starry-abyss
Copy link
Contributor

@Ranoller Note if you disable v-sync, the issue may disappear too. So when testing this, one shouldn't disable v-sync.
What is "internal v-sync"? V-sync off + "force fps 60"?

@Ranoller
Copy link
Contributor

Ranoller commented Nov 18, 2019

In my demo? There is not force fps, is that:

Button -> Godot Vsync (Internal):
OS.vsync_enabled = true
OS.vsync_via_compositor = false

Button -> Windows Vsync (compositor):
OS.vsync_enabled = true
OS.vsync_via_compositor = true

Button -> No Vsync:
OS.vsync_enabled = false
OS.vsync_via_compositor = false

Button -> Toggle Screen Mode:
Toggle between: Full Screen, Windowed Maximized and Windowed.

With buttons "Godot Vsync" and "Windows Vsync" you should get 60, 120 fps or whatever report your screen. Difference consists in this commit, vsync with or without compositor, to test the differences (This commit merged is needed)

If you dissable vsync the issues disappear, of course, but tearing can happend too, but maybe is good to test all the options, no-vsync included, is valid option for players that configure manually their vertical sincronization, i think.

Sorry if terminology (internal, compositor, etc...)in the demo is confusing. I´m not expert in that. If some definition is bad, demo can be changed and re-uploaded.

(The demo is a re-do of other demo made with a car sprites to test some years ago that i can´t find now, was for godot 2.1 i think)

@starry-abyss
Copy link
Contributor

starry-abyss commented Nov 18, 2019

Yes, was confused with the name, since regular monitor v-sync is not a Godot's internal thing. %)
The demo looks great, I wonder if it even includes modes @lawnjelly was concerned about (so we could have a united test project for all things jitter, without everyone making their own projects)?

@distantforest1
Copy link

distantforest1 commented Dec 4, 2019

Just tested @Calinou's build on my small games, and the stuttering is gone! I've been worrying about this for a while and was even thinking of switching engines... (my thinking was if it lags on small tests, why spend time on making a game on godot?)

Just a question though, Are there any performance hits with turning this on? (ie should we turn it off when its full screen mode or deployed on android?)

@TerminalJack
Copy link
Contributor Author

Just a question though, Are there any performance hits with turning this on? (ie should we turn it off when its full screen mode or deployed on android?)

@keenfoong This setting currently only affects (non-UWP) Windows targets so it won't matter whether it is enabled or not in the case of android or other platforms.

On Windows, there won't be any performance hits when running in full screen mode, regardless of whether this setting is enabled or not. When the game is in full screen mode the vertical sync will be done via OpenGL and this feature won't come into play.

If you are deploying a Windows game and it runs in windowed mode and you are concerned about performance then you may want to consider giving the end-user an option via the UI to disable this feature. If the end-user's system isn't affected by the problem then the game may perform better with this option disabled.

Even if you don't provide a UI option to control this feature then the end-user can still enable or disable it via one of the following command line options when launching the game...

--enable-vsync-via-compositor    When vsync is enabled, vsync via the OS' window compositor (Windows only).
--disable-vsync-via-compositor   Disable vsync via the OS' window compositor (Windows only).

This way if you ship with the feature disabled by default and you get reports about stuttering then you can instruct the affected end-users to enable this feature via --enable-vsync-via-compositor. Conversely, if you ship with the feature enabled by default and you believe it may be causing problems with certain end-users then you can have them disable it via --disable-vsync-via-compositor.

@lawnjelly
Copy link
Member

Sorry I missed this (must have been away that week, and there have been several PRs).. But just to say myself I'm fine to trial this now the command line switch is available. For something like this I was seeing the main risk when first introducing it is whether it gives a worse experience for some users, but making it switchable solves that. 👍

The graphs were interesting! They beg the question, I wonder if there are any power saving settings that affect this (in the GPU or the OS)? e.g.
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/saving-energy-with-vsync-control

@@ -890,6 +890,9 @@
<member name="vsync_enabled" type="bool" setter="set_use_vsync" getter="is_vsync_enabled" default="true">
If [code]true[/code], vertical synchronization (Vsync) is enabled.
</member>
<member name="vsync_via_compositor" type="bool" setter="set_vsync_via_compositor" getter="is_vsync_via_compositor_enabled" default="false">
If [code]true[/code] and [code]vsync_enabled[/code] is true, the operating system's window compositor will be used for vsync when the compositor is enabled and the game is in windowed mode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add:

[b]Note:[/b] This property is implemented on Windows.

following what was done in #34087?

Copy link
Member

@akien-mga akien-mga Dec 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do this fixup in a follow-up commit with other unrelated changes.

@@ -221,7 +221,8 @@ def configure_msvc(env, manual_msvc_config):

LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32',
'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32',
'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt','Avrt']
'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt','Avrt',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a space before 'Avrt',. It's not from your commit but it would be worth fixing nevertheless (maybe also put it on the new line with 'dwmapi' to keep a consistency line wrapping).

@akien-mga akien-mga modified the milestones: 4.0, 3.2 Dec 4, 2019
@akien-mga
Copy link
Member

Given the good feedback and the relatively low risk in this feature's implementation, let's merge :)

It should provide a good stopgap solution for 3.2, at least until we can investigate further and better understand what is involved exactly.

@akien-mga akien-mga merged commit 253efbd into godotengine:master Dec 4, 2019
@akien-mga
Copy link
Member

Thanks for the great work! :)

@akien-mga
Copy link
Member

I had missed that the PR had 3 commits, including a merge commit, which makes the Git history quite hard to follow (you can't tell at a glance which of the 3 commits actually implements the patch since they all have the same diff...). I force pushed an update with a squashed commit: e1dda51

@distantforest1
Copy link

This is great, following some of the other threads about stuttering this has been plaguing people for many years already. People are gonna be happy to hear that this will fix that problem. =) Thanks for the great work

@Ranoller
Copy link
Contributor

Ranoller commented Dec 4, 2019

Yes, this fix one of the stutter problems... not all of them. But that first impression that some users had from godot in their first sessions, that a simple sprite moving in screen will stutters a lot, was very injurious to the capabilities perception of the engine. So, good news here.

@Calinou
Copy link
Member

Calinou commented Dec 4, 2019

@Ranoller The rest of the stuttering issues could likely be fixed by adding semi-fixed timestep or fixed step interpolation.

@neikeq
Copy link
Contributor

neikeq commented Dec 5, 2019

Why is this not enabled by default?

@Zireael07
Copy link
Contributor

I believe the reasoning for that should be in the duplicate PR (the first attempt).

@lawnjelly
Copy link
Member

Why is this not enabled by default?

Presumably because of the risk of it being detrimental on some systems. With a rollout of this type, it makes sense to initially have it as an option. After testing with a wider audience doesn't show negative effects, it is trivial to do another PR to set it as default.

Note that game authors can set it as the default for the game they release currently.

@Calinou
Copy link
Member

Calinou commented Dec 5, 2019

@neikeq We could consider enabling this by default in a 3.2 point release or 4.0. As @lawnjelly pointed out, we need further testing before we can enable it on all systems.

Calinou added a commit to Calinou/godot that referenced this pull request Jan 9, 2020
This feature was added in godotengine#33414 but it was disabled by default.
Now that it got some testing, it's probably safe to enable it
by default.
@hoontee
Copy link
Contributor

hoontee commented Jan 14, 2020

@Ranoller @TerminalJack I've found that the stuttering only occurs when vsync is set to "always off" in graphics card settings. With vsync_via_compositor disabled, does the stuttering disappear after making this change?

image

My findings:

Effect: Stuttering
GPU vsync always off
use_vsync=true/false
vsync_via_compositor=false

Effect: Stuttering
GPU vsync application decides
use_vsync=false
vsync_via_compositor=true/false

Effect: Half refresh rate
GPU vsync always on
use_vsync=true
vsync_via_compositor=true

Kleadron added a commit to Kleadron-Software/GDXP that referenced this pull request Oct 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Very bad FPS stability. Heavy stuttering issue in simple 2D game [Windows 10, Nvidia]