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

This commit is contained in:
TerminalJack 2019-11-06 20:18:55 -06:00 committed by Rémi Verschelde
parent 10481046e5
commit e1dda5195c
11 changed files with 108 additions and 4 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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;
} }

View File

@ -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();

View File

@ -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.

View File

@ -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; )">

View File

@ -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);

View File

@ -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() {

View File

@ -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();

View File

@ -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)])

View File

@ -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);