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

Vsync via the Windows compositor when appropriate. #33145

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions core/bind/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,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());
}
Expand Down Expand Up @@ -1322,6 +1331,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);
Expand All @@ -1336,6 +1348,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::BOOL, "keep_screen_on"), "set_keep_screen_on", "is_keep_screen_on");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_window_size"), "set_min_window_size", "get_min_window_size");
Expand All @@ -1357,6 +1370,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("keep_screen_on", true);
ADD_PROPERTY_DEFAULT("min_window_size", Vector2());
Expand Down
3 changes: 3 additions & 0 deletions core/bind/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ class _OS : public Object {
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();
Expand Down
8 changes: 8 additions & 0 deletions core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,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;
}
Expand Down
8 changes: 7 additions & 1 deletion core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class OS {
bool _allow_hidpi;
bool _allow_layered;
bool _use_vsync;
bool _vsync_via_compositor;

char *last_error;

Expand Down Expand Up @@ -100,9 +101,10 @@ class OS {
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;
Expand All @@ -111,6 +113,7 @@ class OS {
maximized = p_maximized;
always_on_top = p_always_on_top;
use_vsync = p_use_vsync;
vsync_via_compositor = p_vsync_via_compositor;
layered = false;
}
};
Expand Down Expand Up @@ -513,6 +516,9 @@ class OS {
//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();
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/OS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</member>
<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window" default="false">
If [code]true[/code], removes the window frame.
[b]Note:[/b] Setting [code]window_borderless[/code] to [code]false[/code] disables per-pixel transparency.
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@
<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).
</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>
<member name="editor/search_in_file_extensions" type="PoolStringArray" setter="" getter="" default="PoolStringArray( &quot;gd&quot;, &quot;shader&quot; )">
Expand Down
21 changes: 21 additions & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ void Main::print_help(const char *p_binary) {
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(" --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

Expand Down Expand Up @@ -397,6 +399,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Vector<String> 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
Expand Down Expand Up @@ -588,6 +591,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

Expand Down Expand Up @@ -1007,6 +1018,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);

Expand Down
41 changes: 40 additions & 1 deletion platform/windows/context_gl_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

#include "context_gl_windows.h"

#include <dwmapi.h>

#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
Expand Down Expand Up @@ -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;
}
Comment on lines +78 to +80
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to call this function in every frame?

On Windows 8+ it's always true, and for Windows 7 it might be better to call it once at the start and then add WM_DWMCOMPOSITIONCHANGED message handler. See DwmIsCompositionEnabled docs for more info.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is what glfw and Chromium do. DwmIsCompositionEnabled() is being called only when vsync is enabled so the thread will block when it calls either Dwmflush() or SwapBuffers(). Because of this, I don't know if it is worth the trouble to listen for WM_DWMCOMPOSITIONCHANGED.


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

Expand Down Expand Up @@ -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() {
Expand Down
3 changes: 3 additions & 0 deletions platform/windows/context_gl_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
3 changes: 2 additions & 1 deletion platform/windows/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions platform/windows/os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,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);
Expand Down