diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 1544503045c..4c8dcc20ea0 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -584,6 +584,15 @@ bool _OS::is_vsync_enabled() const {
return OS::get_singleton()->is_vsync_enabled();
}
+void _OS::set_vsync_via_compositor(bool p_enable) {
+ OS::get_singleton()->set_vsync_via_compositor(p_enable);
+}
+
+bool _OS::is_vsync_via_compositor_enabled() const {
+
+ return OS::get_singleton()->is_vsync_via_compositor_enabled();
+}
+
_OS::PowerState _OS::get_power_state() {
return _OS::PowerState(OS::get_singleton()->get_power_state());
}
@@ -1335,6 +1344,9 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_vsync", "enable"), &_OS::set_use_vsync);
ClassDB::bind_method(D_METHOD("is_vsync_enabled"), &_OS::is_vsync_enabled);
+ ClassDB::bind_method(D_METHOD("set_vsync_via_compositor", "enable"), &_OS::set_vsync_via_compositor);
+ ClassDB::bind_method(D_METHOD("is_vsync_via_compositor_enabled"), &_OS::is_vsync_via_compositor_enabled);
+
ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &_OS::has_feature);
ClassDB::bind_method(D_METHOD("get_power_state"), &_OS::get_power_state);
@@ -1349,6 +1361,7 @@ void _OS::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen");
ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vsync_enabled"), "set_use_vsync", "is_vsync_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vsync_via_compositor"), "set_vsync_via_compositor", "is_vsync_via_compositor_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_screen_on"), "set_keep_screen_on", "is_keep_screen_on");
@@ -1371,6 +1384,7 @@ void _OS::_bind_methods() {
ADD_PROPERTY_DEFAULT("current_screen", 0);
ADD_PROPERTY_DEFAULT("exit_code", 0);
ADD_PROPERTY_DEFAULT("vsync_enabled", true);
+ ADD_PROPERTY_DEFAULT("vsync_via_compositor", false);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
ADD_PROPERTY_DEFAULT("keep_screen_on", true);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 18182860c66..d57da36ca5c 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -345,6 +345,9 @@ public:
void set_use_vsync(bool p_enable);
bool is_vsync_enabled() const;
+ void set_vsync_via_compositor(bool p_enable);
+ bool is_vsync_via_compositor_enabled() const;
+
PowerState get_power_state();
int get_power_seconds_left();
int get_power_percent_left();
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 25889de1b30..2d61417b4fd 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -572,6 +572,14 @@ bool OS::is_vsync_enabled() const {
return _use_vsync;
}
+void OS::set_vsync_via_compositor(bool p_enable) {
+ _vsync_via_compositor = p_enable;
+}
+
+bool OS::is_vsync_via_compositor_enabled() const {
+ return _vsync_via_compositor;
+}
+
OS::PowerState OS::get_power_state() {
return POWERSTATE_UNKNOWN;
}
diff --git a/core/os/os.h b/core/os/os.h
index 687ccaaba5e..26db629d7c2 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -60,6 +60,9 @@ class OS {
bool _allow_hidpi;
bool _allow_layered;
bool _use_vsync;
+ bool _vsync_via_compositor;
+
+ char *last_error;
void *_stack_bottom;
@@ -98,9 +101,10 @@ public:
bool maximized;
bool always_on_top;
bool use_vsync;
+ bool vsync_via_compositor;
bool layered;
float get_aspect() const { return (float)width / (float)height; }
- VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) {
+ VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false, bool p_vsync_via_compositor = false) {
width = p_width;
height = p_height;
fullscreen = p_fullscreen;
@@ -109,6 +113,7 @@ public:
maximized = p_maximized;
always_on_top = p_always_on_top;
use_vsync = p_use_vsync;
+ vsync_via_compositor = p_vsync_via_compositor;
layered = false;
}
};
@@ -507,6 +512,9 @@ public:
//real, actual overridable function to switch vsync, which needs to be called from graphics thread if needed
virtual void _set_use_vsync(bool p_enable) {}
+ void set_vsync_via_compositor(bool p_enable);
+ bool is_vsync_via_compositor_enabled() const;
+
virtual OS::PowerState get_power_state();
virtual int get_power_seconds_left();
virtual int get_power_percent_left();
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 331723df7cc..54b4f3df64c 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -928,6 +928,9 @@
If [code]true[/code], vertical synchronization (Vsync) is enabled.
+
+ 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.
+
If [code]true[/code], removes the window frame.
[b]Note:[/b] Setting [code]window_borderless[/code] to [code]false[/code] disables per-pixel transparency.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 772c2f5073c..97af3ec77af 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -431,6 +431,9 @@
If [code]true[/code], enables vertical synchronization. This eliminates tearing that may appear in moving scenes, at the cost of higher input latency and stuttering at lower framerates. If [code]false[/code], vertical synchronization will be disabled, however, many platforms will enforce it regardless (such as mobile platforms and HTML5).
+
+ If [code]Use Vsync[/code] is enabled and this setting is [code]true[/code], enables vertical synchronization via the operating system's window compositor when in windowed mode and the compositor is enabled. This will prevent stutter in certain situations. (Windows only.)
+
diff --git a/main/main.cpp b/main/main.cpp
index 724f8206d0a..cdaee061359 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -259,6 +259,8 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --position , Request window position.\n");
OS::get_singleton()->print(" --low-dpi Force low-DPI mode (macOS and Windows only).\n");
OS::get_singleton()->print(" --no-window Disable window creation (Windows only). Useful together with --script.\n");
+ OS::get_singleton()->print(" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n");
+ OS::get_singleton()->print(" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n");
OS::get_singleton()->print("\n");
#endif
@@ -399,6 +401,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Vector breakpoints;
bool use_custom_res = true;
bool force_res = false;
+ bool saw_vsync_via_compositor_override = false;
#ifdef TOOLS_ENABLED
bool found_project = false;
#endif
@@ -590,6 +593,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--no-window") { // disable window creation (Windows only)
OS::get_singleton()->set_no_window_mode(true);
+ } else if (I->get() == "--enable-vsync-via-compositor") {
+
+ video_mode.vsync_via_compositor = true;
+ saw_vsync_via_compositor_override = true;
+ } else if (I->get() == "--disable-vsync-via-compositor") {
+
+ video_mode.vsync_via_compositor = false;
+ saw_vsync_via_compositor_override = true;
#endif
} else if (I->get() == "--profiling") { // enable profiling
@@ -1009,6 +1020,16 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
video_mode.use_vsync = GLOBAL_DEF_RST("display/window/vsync/use_vsync", true);
OS::get_singleton()->_use_vsync = video_mode.use_vsync;
+ if (!saw_vsync_via_compositor_override) {
+ // If one of the command line options to enable/disable vsync via the
+ // window compositor ("--enable-vsync-via-compositor" or
+ // "--disable-vsync-via-compositor") was present then it overrides the
+ // project setting.
+ video_mode.vsync_via_compositor = GLOBAL_DEF("display/window/vsync/vsync_via_compositor", false);
+ }
+
+ OS::get_singleton()->_vsync_via_compositor = video_mode.vsync_via_compositor;
+
OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false);
video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false);
diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp
index e715999378f..34a56983949 100644
--- a/platform/windows/context_gl_windows.cpp
+++ b/platform/windows/context_gl_windows.cpp
@@ -34,6 +34,8 @@
#include "context_gl_windows.h"
+#include
+
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
@@ -63,16 +65,52 @@ int ContextGL_Windows::get_window_height() {
return OS::get_singleton()->get_video_mode().height;
}
+bool ContextGL_Windows::should_vsync_via_compositor() {
+
+ if (OS::get_singleton()->is_window_fullscreen() || !OS::get_singleton()->is_vsync_via_compositor_enabled()) {
+ return false;
+ }
+
+ // Note: All Windows versions supported by Godot have a compositor.
+ // It can be disabled on earlier Windows versions.
+ BOOL dwm_enabled;
+
+ if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled))) {
+ return dwm_enabled;
+ }
+
+ return false;
+}
+
void ContextGL_Windows::swap_buffers() {
SwapBuffers(hDC);
+
+ if (use_vsync) {
+ bool vsync_via_compositor_now = should_vsync_via_compositor();
+
+ if (vsync_via_compositor_now) {
+ DwmFlush();
+ }
+
+ if (vsync_via_compositor_now != vsync_via_compositor) {
+ // The previous frame had a different operating mode than this
+ // frame. Set the 'vsync_via_compositor' member variable and the
+ // OpenGL swap interval to their proper values.
+ set_use_vsync(true);
+ }
+ }
}
void ContextGL_Windows::set_use_vsync(bool p_use) {
+ vsync_via_compositor = p_use && should_vsync_via_compositor();
+
if (wglSwapIntervalEXT) {
- wglSwapIntervalEXT(p_use ? 1 : 0);
+ int swap_interval = (p_use && !vsync_via_compositor) ? 1 : 0;
+ wglSwapIntervalEXT(swap_interval);
}
+
use_vsync = p_use;
}
@@ -177,6 +215,7 @@ ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) {
opengl_3_context = p_opengl_3_context;
hWnd = hwnd;
use_vsync = false;
+ vsync_via_compositor = false;
}
ContextGL_Windows::~ContextGL_Windows() {
diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h
index d23fba50e1f..e65ea1928f3 100644
--- a/platform/windows/context_gl_windows.h
+++ b/platform/windows/context_gl_windows.h
@@ -50,9 +50,12 @@ class ContextGL_Windows {
HWND hWnd;
bool opengl_3_context;
bool use_vsync;
+ bool vsync_via_compositor;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
+ static bool should_vsync_via_compositor();
+
public:
void release_current();
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 9a2b2bcb989..b37a21f37fd 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -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',
+ 'dwmapi']
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
if manual_msvc_config:
@@ -348,7 +349,7 @@ def configure_mingw(env):
env.Append(CCFLAGS=['-mwindows'])
env.Append(CPPDEFINES=['WINDOWS_ENABLED', 'OPENGL_ENABLED', 'WASAPI_ENABLED', 'WINMIDI_ENABLED'])
env.Append(CPPDEFINES=[('WINVER', env['target_win_version']), ('_WIN32_WINNT', env['target_win_version'])])
- env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt', 'avrt', 'uuid'])
+ env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt', 'avrt', 'uuid', 'dwmapi'])
env.Append(CPPDEFINES=['MINGW_ENABLED', ('MINGW_HAS_SECURE_API', 1)])
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 9c1514f5417..f749134f3cc 100755
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1480,6 +1480,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
video_driver_index = p_video_driver;
gl_context->set_use_vsync(video_mode.use_vsync);
+ set_vsync_via_compositor(video_mode.vsync_via_compositor);
#endif
visual_server = memnew(VisualServerRaster);