Browse Source

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

TerminalJack 5 years ago
parent
commit
e1dda5195c

+ 14 - 0
core/bind/core_bind.cpp

@@ -584,6 +584,15 @@ bool _OS::is_vsync_enabled() const {
 	return OS::get_singleton()->is_vsync_enabled();
 	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() {
 _OS::PowerState _OS::get_power_state() {
 	return _OS::PowerState(OS::get_singleton()->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("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("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("has_feature", "tag_name"), &_OS::has_feature);
 
 
 	ClassDB::bind_method(D_METHOD("get_power_state"), &_OS::get_power_state);
 	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, "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::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_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::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::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");
 	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("current_screen", 0);
 	ADD_PROPERTY_DEFAULT("exit_code", 0);
 	ADD_PROPERTY_DEFAULT("exit_code", 0);
 	ADD_PROPERTY_DEFAULT("vsync_enabled", true);
 	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", false);
 	ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
 	ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
 	ADD_PROPERTY_DEFAULT("keep_screen_on", true);
 	ADD_PROPERTY_DEFAULT("keep_screen_on", true);

+ 3 - 0
core/bind/core_bind.h

@@ -345,6 +345,9 @@ public:
 	void set_use_vsync(bool p_enable);
 	void set_use_vsync(bool p_enable);
 	bool is_vsync_enabled() const;
 	bool is_vsync_enabled() const;
 
 
+	void set_vsync_via_compositor(bool p_enable);
+	bool is_vsync_via_compositor_enabled() const;
+
 	PowerState get_power_state();
 	PowerState get_power_state();
 	int get_power_seconds_left();
 	int get_power_seconds_left();
 	int get_power_percent_left();
 	int get_power_percent_left();

+ 8 - 0
core/os/os.cpp

@@ -572,6 +572,14 @@ bool OS::is_vsync_enabled() const {
 	return _use_vsync;
 	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() {
 OS::PowerState OS::get_power_state() {
 	return POWERSTATE_UNKNOWN;
 	return POWERSTATE_UNKNOWN;
 }
 }

+ 9 - 1
core/os/os.h

@@ -60,6 +60,9 @@ class OS {
 	bool _allow_hidpi;
 	bool _allow_hidpi;
 	bool _allow_layered;
 	bool _allow_layered;
 	bool _use_vsync;
 	bool _use_vsync;
+	bool _vsync_via_compositor;
+
+	char *last_error;
 
 
 	void *_stack_bottom;
 	void *_stack_bottom;
 
 
@@ -98,9 +101,10 @@ public:
 		bool maximized;
 		bool maximized;
 		bool always_on_top;
 		bool always_on_top;
 		bool use_vsync;
 		bool use_vsync;
+		bool vsync_via_compositor;
 		bool layered;
 		bool layered;
 		float get_aspect() const { return (float)width / (float)height; }
 		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;
 			width = p_width;
 			height = p_height;
 			height = p_height;
 			fullscreen = p_fullscreen;
 			fullscreen = p_fullscreen;
@@ -109,6 +113,7 @@ public:
 			maximized = p_maximized;
 			maximized = p_maximized;
 			always_on_top = p_always_on_top;
 			always_on_top = p_always_on_top;
 			use_vsync = p_use_vsync;
 			use_vsync = p_use_vsync;
+			vsync_via_compositor = p_vsync_via_compositor;
 			layered = false;
 			layered = false;
 		}
 		}
 	};
 	};
@@ -507,6 +512,9 @@ public:
 	//real, actual overridable function to switch vsync, which needs to be called from graphics thread if needed
 	//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) {}
 	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 OS::PowerState get_power_state();
 	virtual int get_power_seconds_left();
 	virtual int get_power_seconds_left();
 	virtual int get_power_percent_left();
 	virtual int get_power_percent_left();

+ 3 - 0
doc/classes/OS.xml

@@ -928,6 +928,9 @@
 		<member name="vsync_enabled" type="bool" setter="set_use_vsync" getter="is_vsync_enabled" default="true">
 		<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.
 			If [code]true[/code], vertical synchronization (Vsync) is enabled.
 		</member>
 		</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.
+		</member>
 		<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window" default="false">
 		<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window" default="false">
 			If [code]true[/code], removes the window frame.
 			If [code]true[/code], removes the window frame.
 			[b]Note:[/b] Setting [code]window_borderless[/code] to [code]false[/code] disables per-pixel transparency.
 			[b]Note:[/b] Setting [code]window_borderless[/code] to [code]false[/code] disables per-pixel transparency.

+ 3 - 0
doc/classes/ProjectSettings.xml

@@ -431,6 +431,9 @@
 		<member name="display/window/vsync/use_vsync" type="bool" setter="" getter="" default="true">
 		<member name="display/window/vsync/use_vsync" type="bool" setter="" getter="" default="true">
 			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]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).
 		</member>
 		</member>
+		<member name="display/window/vsync/vsync_via_compositor" type="bool" setter="" getter="" default="false">
+			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.)
+		</member>
 		<member name="editor/script_templates_search_path" type="String" setter="" getter="" default="&quot;res://script_templates&quot;">
 		<member name="editor/script_templates_search_path" type="String" setter="" getter="" default="&quot;res://script_templates&quot;">
 		</member>
 		</member>
 		<member name="editor/search_in_file_extensions" type="PoolStringArray" setter="" getter="" default="PoolStringArray( &quot;gd&quot;, &quot;shader&quot; )">
 		<member name="editor/search_in_file_extensions" type="PoolStringArray" setter="" getter="" default="PoolStringArray( &quot;gd&quot;, &quot;shader&quot; )">

+ 21 - 0
main/main.cpp

@@ -259,6 +259,8 @@ void Main::print_help(const char *p_binary) {
 	OS::get_singleton()->print("  --position <X>,<Y>               Request window position.\n");
 	OS::get_singleton()->print("  --position <X>,<Y>               Request window position.\n");
 	OS::get_singleton()->print("  --low-dpi                        Force low-DPI mode (macOS and Windows only).\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("  --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");
 	OS::get_singleton()->print("\n");
 #endif
 #endif
 
 
@@ -399,6 +401,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	Vector<String> breakpoints;
 	Vector<String> breakpoints;
 	bool use_custom_res = true;
 	bool use_custom_res = true;
 	bool force_res = false;
 	bool force_res = false;
+	bool saw_vsync_via_compositor_override = false;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	bool found_project = false;
 	bool found_project = false;
 #endif
 #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)
 		} else if (I->get() == "--no-window") { // disable window creation (Windows only)
 
 
 			OS::get_singleton()->set_no_window_mode(true);
 			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
 #endif
 		} else if (I->get() == "--profiling") { // enable profiling
 		} 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);
 	video_mode.use_vsync = GLOBAL_DEF_RST("display/window/vsync/use_vsync", true);
 	OS::get_singleton()->_use_vsync = video_mode.use_vsync;
 	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);
 	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);
 	video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false);
 
 

+ 40 - 1
platform/windows/context_gl_windows.cpp

@@ -34,6 +34,8 @@
 
 
 #include "context_gl_windows.h"
 #include "context_gl_windows.h"
 
 
+#include <dwmapi.h>
+
 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
 #define WGL_CONTEXT_FLAGS_ARB 0x2094
 #define WGL_CONTEXT_FLAGS_ARB 0x2094
@@ -63,16 +65,52 @@ int ContextGL_Windows::get_window_height() {
 	return OS::get_singleton()->get_video_mode().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() {
 void ContextGL_Windows::swap_buffers() {
 
 
 	SwapBuffers(hDC);
 	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) {
 void ContextGL_Windows::set_use_vsync(bool p_use) {
 
 
+	vsync_via_compositor = p_use && should_vsync_via_compositor();
+
 	if (wglSwapIntervalEXT) {
 	if (wglSwapIntervalEXT) {
-		wglSwapIntervalEXT(p_use ? 1 : 0);
+		int swap_interval = (p_use && !vsync_via_compositor) ? 1 : 0;
+		wglSwapIntervalEXT(swap_interval);
 	}
 	}
+
 	use_vsync = p_use;
 	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;
 	opengl_3_context = p_opengl_3_context;
 	hWnd = hwnd;
 	hWnd = hwnd;
 	use_vsync = false;
 	use_vsync = false;
+	vsync_via_compositor = false;
 }
 }
 
 
 ContextGL_Windows::~ContextGL_Windows() {
 ContextGL_Windows::~ContextGL_Windows() {

+ 3 - 0
platform/windows/context_gl_windows.h

@@ -50,9 +50,12 @@ class ContextGL_Windows {
 	HWND hWnd;
 	HWND hWnd;
 	bool opengl_3_context;
 	bool opengl_3_context;
 	bool use_vsync;
 	bool use_vsync;
+	bool vsync_via_compositor;
 
 
 	PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
 	PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
 
 
+	static bool should_vsync_via_compositor();
+
 public:
 public:
 	void release_current();
 	void release_current();
 
 

+ 3 - 2
platform/windows/detect.py

@@ -221,7 +221,8 @@ def configure_msvc(env, manual_msvc_config):
 
 
     LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32',
     LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32',
             'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32',
             '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])
     env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
 
 
     if manual_msvc_config:
     if manual_msvc_config:
@@ -348,7 +349,7 @@ def configure_mingw(env):
     env.Append(CCFLAGS=['-mwindows'])
     env.Append(CCFLAGS=['-mwindows'])
     env.Append(CPPDEFINES=['WINDOWS_ENABLED', 'OPENGL_ENABLED', 'WASAPI_ENABLED', 'WINMIDI_ENABLED'])
     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(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)])
     env.Append(CPPDEFINES=['MINGW_ENABLED', ('MINGW_HAS_SECURE_API', 1)])
 
 

+ 1 - 0
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;
 	video_driver_index = p_video_driver;
 
 
 	gl_context->set_use_vsync(video_mode.use_vsync);
 	gl_context->set_use_vsync(video_mode.use_vsync);
+	set_vsync_via_compositor(video_mode.vsync_via_compositor);
 #endif
 #endif
 
 
 	visual_server = memnew(VisualServerRaster);
 	visual_server = memnew(VisualServerRaster);